#include "DataConnection.h"
#include "FtpDataClient.h"
#include "FtpDataServer.h"
#include "FtpSession.h"

#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/SocketsOps.h"

#include <memory>

using namespace ftp;
using namespace muduo;
using namespace muduo::net;

DataConnection::DataConnection(std::shared_ptr<FtpSession> session,
	muduo::net::EventLoop* loop,
	TransferMode mode,
	const muduo::net::InetAddress& serverAddr,
	const std::string& nameArg)
	: weakSession_(session),
	loop_(CHECK_NOTNULL(loop)),
	mode_(mode),
	serverAddr_(serverAddr),
	controlConnName_(nameArg),
	connectionCallback_(defaultConnectionCallback),
	messageCallback_(defaultMessageCallback),
	connected_(false)
{
	LOG_DEBUG << "Create a new DataConnection obj@" << this;

	if (mode == TransferMode::PORT) {
		client_.reset(new FtpDataClient(loop, serverAddr, nameArg));
	}
	else { // TransferMode::PASV
		// do nothing
	}
}

DataConnection::~DataConnection()
{
	LOG_DEBUG << "Destory DataConnection object@" << this;
}

void DataConnection::listen()
{
	if (mode_ == TransferMode::PASV) {
		server_.reset(new FtpDataServer(loop_, serverAddr_));
		// Set event callbacks
#if 0
		server_->setConnectionCallback(connectionCallback_);
		server_->setWriteCompleteCallback(writeCompleteCallback_);
		server_->setMessageCallback(messageCallback_);
#else
		server_->setConnectionCallback(std::bind(&DataConnection::onConnection, this, _1));
		server_->setWriteCompleteCallback(std::bind(&DataConnection::onWriteComplete, this, _1));
		server_->setMessageCallback(std::bind(&DataConnection::onMessage, this, _1, _2, _3));
#endif

		server_->start();
		client_.reset();
	}
}

bool DataConnection::isListening()
{
	if (get_pointer(server_)) {
		return server_->listening();
	}
	return false;
}

InetAddress DataConnection::listenAddress() const
{
	if (get_pointer(server_)) {
		return server_->listenAddress();
	}
	return InetAddress();
}

void DataConnection::connect()
{
	if (mode_ == TransferMode::PORT) {
		// Set event callbacks
		client_->setConnectionCallback(connectionCallback_);
		client_->setWriteCompleteCallback(writeCompleteCallback_);
		client_->setMessageCallback(messageCallback_);

		client_->connect();
		server_.reset();
	}
}

void DataConnection::disconnect()
{
	if (mode_ == TransferMode::PORT) {
		client_->disconnect();
	}
}

void DataConnection::stop()
{
	if (mode_ == TransferMode::PORT) {
		client_->stop();
	}
}

bool DataConnection::isOpened()
{
	if (mode_ == TransferMode::PORT) {
		if (get_pointer(client_)) {
			return true;
		}
	}
	else {
		if (get_pointer(server_)) {
			return server_->listening();
		}
	}
	return false;
}

void DataConnection::setCallbacks(muduo::net::ConnectionCallback conncb,
	muduo::net::MessageCallback msgcb,
	muduo::net::WriteCompleteCallback writecb)
{
	if (conncb) setConnectionCallback(conncb);
	if (msgcb) setMessageCallback(msgcb);
	if (writecb) setWriteCompleteCallback(writecb);
}


void DataConnection::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
	if (connectionCallback_) {
		connectionCallback_(conn);
	}
}

void DataConnection::onMessage(const muduo::net::TcpConnectionPtr& conn,
	muduo::net::Buffer* buffer,
	muduo::Timestamp receiveTime)
{
	if (messageCallback_) {
		messageCallback_(conn, buffer, receiveTime);
	}
}

void DataConnection::onWriteComplete(const muduo::net::TcpConnectionPtr& conn)
{
	if (writeCompleteCallback_) {
		writeCompleteCallback_(conn);
	}
}